home *** CD-ROM | disk | FTP | other *** search
-
- /* SerialCommDevice.m
-
- Synopsis:
- This class defines an easy to use, robust, fault-tollerant, full-duplex serial
- communications protocol. The implementation uses a packet-based protocol that
- transmits only printable ascii characters.
-
- Description:
- The actual transmission protocol only allows for the transfer of printable ascii
- characters. When necessary, any non-printable characters are transparently en/decoded
- to effectively support the transfer of all 8-bits worth of information contained w/in
- each byte. This encoding scheme isn't particularly efficient in that any non-printable
- ascii characters end up requiring two characters for their internal representation.
- For instance, a packet containing all '\0' characters, would end up taking twice as
- long to transfer as a packet containing the same number of 'a' characters 8^(. Future
- enhancements should use a more efficient encoding scheme or a mechanism for compressing
- the data before transmission. Additionally, the protocol and encoding schemes reserve the
- following five printable characters: '<', '>', '$', '%', and '&'. Therefore, these
- characters end up requiring the same two character encoding scheme as the non-printable
- ascii characters.
-
- Each packet has the following format:
-
- Offset Size Description
- ------ ---- ------------------------------------------------------------------
- 0 1 SOP_CHAR_CODE '<' Defines start of packet.
- 1 1 Packet Type Field.
- 2 1 Packet Sequence character - to distinguish packets @ receiver.
- <0 or more data bytes here>
- n-CSUM_CHARS CSUM_CHARS hex-encoded checksum of bytes 1 thru n-1 inclusive.
- n 1 EOP_CHAR_CODE '>' Defines end of packet.
-
- where the following Packet Types are currently defined:
- D - DATA Packet -
- A - ACK Packet - Acknowledges reciept of specified pkt.
- N - NACK Packet - A bad packet was received - request retransmit.
- T - TEST Packet - Receiver just loops data back - sort of a loop-back test.
- O - OOL Packet - Out-Of-Line data packet - gets sent to remote's delegate via -oolMessage:len:
-
- Additionally, three other chars (MAP0_CHAR_CODE thru MAP2_CHAR_CODE are '$', '%', '&' respectively)
- have special meaning and are used in an encoding scheme that allows all 256 possible character
- codes to map into the printable set of ASCII characters.
-
- */
- #import "SerialCommDevice.h"
- #import <sys/types.h>
- #import <sys/ttydev.h>
- #import <sys/ioctl.h>
- #import <fcntl.h>
- #import <stdio.h>
- #import <limits.h>
- #import <libc.h>
- #import <stdarg.h>
- #import <ctype.h>
- #import <mach/cthreads.h>
- #import <ansi/errno.h>
- /* reserved characters: */
- #define SOP_CHAR_CODE 0x3c /* '<' start of packet character. */
- #define EOP_CHAR_CODE 0x3e /* '>' end of packet character. */
- #define MAP0_CHAR_CODE (0x24) /* '$' maps 0-0x1f,SOP, EOP and MAP0-2 CHAR_CODES into 20 thru 5f. */
- #define MAP1_CHAR_CODE (0x25) /* '%' maps 0x80-0xbf into 20 thru 5f. */
- #define MAP2_CHAR_CODE (0x26) /* '&' maps 0xc0-0xff into 20 thru 5f. */
- #define MAP_CHAR(c) (((u_char)(c)>=MAP0_CHAR_CODE)&&((u_char)(c)<=MAP2_CHAR_CODE))
- #define PKT_CHAR(c) (((c)==SOP_CHAR_CODE)||((c)==EOP_CHAR_CODE))
- #define MAP0_RANGE(c) ((((u_char)(c)>=0)&&(((u_char)c)<=0x1f))||PKT_CHAR(c)||MAP_CHAR(c)||((c)==0x7f))
- #define MAP1_RANGE(c) (((u_char)(c)>=0x80)&&(((u_char)c)<=0xbf))
- #define MAP2_RANGE(c) (((u_char)(c)>=0xc0)&&(((u_char)c)<=0xff))
- #define NORMAL_CHAR(c) (((u_char)(c)>=0x20)&&(((u_char)c)<=0x7e)&&!MAP_CHAR(c)&&!PKT_CHAR(c))
- #define ILLEGAL_CHAR(c) (((u_char)(c)<0x20)||(((u_char)c)>0x7e))
- #define MAP_MASK 0x3f
- #define MAP_OFFSET 0x3f
-
-
- /* checksum may be computed with from 1 to 8 bytes - change all four of the following defines at the same time: */
- #define CSUM_CHARS 4 /* must fit into int (8 max) */
- #define CSUM_MSB 0x8000
- #define CSUM_MASK 0xffff
- #define CSUM_FMT "%04x"
-
- /* defined packet types: */
- #define DATA_PKT 'D'
- #define ACK_PKT 'A'
- #define NACK_PKT 'N'
- #define TEST_PKT 'T'
- #define OOL_PKT 'O'
- #define VALID_PKT_TYPE(c) ((c == DATA_PKT) || (c == ACK_PKT) || (c == NACK_PKT) || (c == TEST_PKT) || (c == OOL_PKT))
-
- #define NEXT_ID_CHAR(pktid) ((pktid++%10)+'0')
- #define NACK_ID '?'
- #define RCV_IN_BASKET ((self->dataPktIndex)?1:0)
- #define RCV_OUT_BASKET (!RCV_IN_BASKET)
-
- /* defaults: */
- #define XMIT_RETRY_COUNT 10
- #define XMIT_TIMEOUT_MSECS 10000
- #define RECV_TIMEOUT_MSECS 0
-
- static int debugFlag = 1;
- static void theListener(SerialCommDevice *self);
-
- /* stringForCharArray()
- Returns a string with printable representation of array of any ascii chars. For debug only.
- */
- static char *stringForCharArray(char *str, int len)
- {
- static char result[128];
- char *cp=result;
- int i;
-
- for (i=0;i<len;i++) {
- if (isprint(str[i]))
- sprintf(cp,"%c",str[i]);
- else
- sprintf(cp,"<0x%02x>",(unsigned)str[i]);
- cp = index(cp,'\0');
- }
- return result;
- }
-
- /* microTimeStampA()
- Accumulative time stamp. Returns a positive integer value of microseconds
- since the since the timer was initialized or a negative number if greater
- than 2048 seconds have elapsed. oldTime is used to keep track of the start
- time. It is therefore important that it be consistant between calls. To
- initialize the timer, call with oldTime.tv_sec == oldTime.tv_usec == 0.
- */
- #define MAX_MTSA 2048000000
- int microTimeStampA(struct timeval * oldTime)
- {
- struct timeval nt;
- long elapsedTime = (-1);
-
- if (gettimeofday(&nt, (struct timezone *) 0) < 0)
- perror("gettimeofday() failed.");
-
- if (oldTime->tv_sec == 0 && oldTime->tv_usec == 0) {
- elapsedTime = 0; /* just initing oldTime struct. */
- *oldTime = nt;
- }
- else {
- elapsedTime = (nt.tv_sec - oldTime->tv_sec) * 1000000;
- if (nt.tv_usec < oldTime->tv_usec)
- elapsedTime += nt.tv_usec + 1000000 - oldTime->tv_usec;
- else
- elapsedTime += nt.tv_usec - oldTime->tv_usec;
- }
- if (elapsedTime > MAX_MTSA)
- elapsedTime = (-1);
-
- return ((int)elapsedTime);
- }
-
- static int baudRateCode(baudRateType br)
- {
- int brc;
- switch (br) {
- case br38400:
- brc=EXTB;break;
- case br19200:
- brc=EXTA;break;
- case br9600:
- brc=B9600;break;
- case br4800:
- brc=4800;break;
- case br2400:
- brc=B2400;break;
- case br1200:
- brc=B1200;break;
- case br600:
- brc=B600;break;
- case br300:
- brc=B300;break;
- case br200:
- brc=B9600;break;
- case br150:
- brc=B150;break;
- case br134:
- brc=B134;break;
- case br110:
- brc=B110;break;
- case br75:
- brc=B75;break;
- case br50:
- brc=B50;break;
- default:
- brc=B9600;break;
- }
- return brc;
- }
-
- char *portString(sccPortType thePort)
- {
- char *p;
-
- if (thePort == sccPortB)
- p = "/dev/ttyb";
- else
- p = "/dev/ttya";
- return p;
- }
-
-
- /* sumCheck()
- add each character to right-rotated previous parital result.
- */
- static int sumCheck(u_char *str, int len)
- {
- register int i,
- checkSum=13;
-
- for (i=0;i<len;i++) {
- if (str[i] & 0x1)
- checkSum += (((str[i] & 0xff) >> 1) | CSUM_MSB) ;
- else
- checkSum += ((str[i] & 0xff) >> 1);
- }
-
- return (checkSum & CSUM_MASK);
- }
-
-
- /* stringEncode()
- Process the binary input string and map all into printable
- ascii in the output string. Returns the length of the output string.
- */
- static int stringEncode(char *input, int len, char *output)
- {
- register int i;
- register char *rPtr, *wPtr;
-
- rPtr = input;
- wPtr = output;
- for (i=0;(i<len)&&((wPtr-input)<(SCD_MAX_PACKETSIZE-(CSUM_CHARS+4)));i++) {
- if (NORMAL_CHAR(*rPtr)) {
- *wPtr++ = *rPtr++;
- } else if (MAP0_RANGE(*rPtr)) {
- *wPtr++ = MAP0_CHAR_CODE;
- *wPtr++ = (*rPtr++ & MAP_MASK) + MAP_OFFSET;
- } else if (MAP1_RANGE(*rPtr)) {
- *wPtr++ = MAP1_CHAR_CODE;
- *wPtr++ = (*rPtr++ & MAP_MASK) + MAP_OFFSET;
- } else if (MAP2_RANGE(*rPtr)) {
- *wPtr++ = MAP2_CHAR_CODE;
- *wPtr++ = (*rPtr++ & MAP_MASK) + MAP_OFFSET;
- }
- }
- return (wPtr-output);
- }
-
-
- /* stringDecode()
- Process the printable ascii string, expanding character MAP sequences
- in the output string. Returns the length of the output string.
- */
- static int stringDecode(char *input, int len, char *output)
- {
- register int i;
- register char *rPtr, *wPtr;
-
- rPtr = input;
- wPtr = output;
- for (i=0;(i<len)&&((wPtr-input)<(SCD_MAX_PACKETSIZE-8));i++,wPtr++,rPtr++) {
- if (NORMAL_CHAR(*rPtr)) {
- *wPtr = *rPtr;
- } else if (*rPtr == MAP0_CHAR_CODE) {
- rPtr++;i++;
- *wPtr = *rPtr - MAP_OFFSET;
- if (*wPtr == 0x3f)
- *wPtr = 0x7f;
- } else if (*rPtr == MAP1_CHAR_CODE) {
- rPtr++;i++;
- *wPtr = (0x80 + *rPtr - MAP_OFFSET);
- } else if (*rPtr == MAP2_CHAR_CODE) {
- rPtr++;i++;
- *wPtr = (0xc0 + *rPtr - MAP_OFFSET);
- }
- }
- return (wPtr-output);
- }
-
- @implementation SerialCommDevice
-
- - setRecvTimeoutMsecs:(int)msecs {recvTimeoutMsecs = msecs;return self;}
- - (int)recvTimeoutMsecs {return recvTimeoutMsecs;}
- - setXmitTimeoutMsecs:(int)msecs {xmitAckTimeoutMsecs = msecs;return self;}
- - (int)xmitTimeoutMsecs {return xmitAckTimeoutMsecs;}
- - setmaxXmitRetries:(int)retries {maxXmitRetries = retries;return self;}
- - (int)maxXmitRetries {return maxXmitRetries;}
-
-
-
-
-
- /* write:len:
- *
- * writes len chars to serial port.
- * Returns zero on success or SCD_IO_ERR on failure.
- */
- -(int)write:(char *)buf len:(int)l
- {
- int retVal=0;
- int written=0;
-
- mutex_lock((mutex_t)writeLock);
- while (written < l) {
- if ((written += write(fd, buf, l-written)) < 0) {
- retVal = SCD_IO_ERR;
- break;
- }
- }
- mutex_unlock((mutex_t)writeLock);
- cthread_yield();
- return retVal;
- }
-
-
- /* - hangOnReadPort:
- *
- * Wait for data available or timeout, whichever occurs first.
- * Returns 0 for success, SCD_IO_ERR for error, and SCD_TIMEOUT_ERR
- * for timeout.
- */
- - (int)hangOnReadPort:(int)timeoutMsecs
- {
- struct timeval to;
- fd_set portList;
- int retVal;
-
- FD_ZERO(&portList);
- FD_SET(fd, &portList);
- if (timeoutMsecs > 0) {
- to.tv_sec = timeoutMsecs / 1000;
- to.tv_usec = (timeoutMsecs % 1000) * 1000;
- retVal = select(fd+1, &portList, 0, 0, &to);
- } else
- retVal = select(fd+1, &portList, 0, 0, 0);
- if (retVal < 0) {
- retVal = SCD_IO_ERR;
- fprintf(stderr,"\nselect() failed:%s\n", strerror(errno));
- }
- else if (retVal > 0)
- retVal = 0;
- else {
- retVal = SCD_TIMEOUT_ERR;
- if (debugFlag)
- fprintf(stderr,"[RTO]");
- }
- return retVal;
- }
-
- /* buildPacket:withID:data:len:buffer:
- *
- * Construct the requested packet in the specified buf.
- * Returns size of the completed packet.
- */
- -(int)buildPacket:(char)pType withID:(char)pktId data:(char *)data len:(int)len buffer:(char *)buf
- {
- int i, retVal;
-
- buf[0] = SOP_CHAR_CODE;
- buf[1] = pType;
- buf[2] = pktId;
- retVal = stringEncode(data, len, &buf[3]) + 2;
- i = sumCheck((u_char *)&buf[1], retVal++);
- sprintf(&buf[retVal], CSUM_FMT, (unsigned)i);
- buf[retVal+CSUM_CHARS] = EOP_CHAR_CODE;
- return (retVal+1+CSUM_CHARS);
- }
-
-
-
-
- /* sendPkt:data:len:pktId:
- *
- * Build and send specified pkt. If not data or test pkt, then send pkt and
- * return zero if successfull. If data or test pkt, wait for reply and return
- * positive microseconds required for transfer if successfull. If retries
- * become exhausted, then return SCD_XFER_ERR, if failure return SCD_IO_ERR.
- */
- -(int)sendPkt:(char)pType data:(char *)data len:(int)len pktId:(char)asciiPktId
- {
- int retries=maxXmitRetries,elapsedTime=0, pktLen, retVal=0;
- char buf[SCD_MAX_PACKETSIZE];
- struct timeval t;
-
- if ((len+CSUM_CHARS+4) > SCD_MAX_PACKETSIZE) {
- fprintf(stderr, "\nsendPkt: data to big. Maximum allowable len is:%d\n", SCD_MAX_PACKETSIZE-(CSUM_CHARS+4));
- retVal = SCD_LENGTH_ERR;
- } else if ((pktLen=[self buildPacket:pType withID:asciiPktId data:data len:len buffer:buf])>SCD_MAX_PACKETSIZE){
- fprintf(stderr, "\nsendPkt: data to big.\n");
- retVal = SCD_LENGTH_ERR;
- } else if ((pType == DATA_PKT) || (pType == TEST_PKT)) { /* wait for response w/these. */
- mutex_lock((mutex_t)dataPktLock);
- asciiAckDPktID = -1;
- if (debugFlag>1)
- fprintf(stderr,"\nSending Pkt:%s\n", stringForCharArray(buf,pktLen));
- t = (struct timeval){0,0};
- for (retVal=0;retVal==0;) {
- if (retVal = [self write:buf len:pktLen])
- break;
- elapsedTime = microTimeStampA(&t); /* set start time. */
- for (;!retVal;) {
- if (asciiAckDPktID == asciiPktId) {
- if (elapsedTime < 0)
- retVal = MAX_MTSA;
- else
- retVal = elapsedTime;
- break;
- }
- else if (nackDPktFlag) {
- retVal=SCD_XFER_ERR;
- if (debugFlag)
- // fprintf(stderr,"[NACK sending:%s]",stringForCharArray(buf,pktLen));
- fprintf(stderr,"[NACK]");
- nackDPktFlag = 0;
- break;
- } else {
- if (xmitAckTimeoutMsecs && (((elapsedTime/1000)>xmitAckTimeoutMsecs) || (elapsedTime < 0))) {
- t = (struct timeval){0,0};
- if (debugFlag)
- // fprintf(stderr,"[XTO sending:%s]",stringForCharArray(buf,pktLen));
- fprintf(stderr,"[XTO]");
- retVal=SCD_XFER_ERR;
- break;
- }
- }
- cthread_yield();
- elapsedTime = microTimeStampA(&t); /* get elapsed time. */
- }
- if (!retVal)
- break;
- else if ((retVal==SCD_XFER_ERR)&&(retries-->0)) {
- retVal = 0;
- }
- }
- mutex_unlock((mutex_t)dataPktLock);
- } else { /* send and forget all other pkt types. */
- if (debugFlag>10)
- fprintf(stderr,"\nSending Pkt:%s\n", stringForCharArray(buf,pktLen));
- retVal = [self write:buf len:pktLen];
- }
- return retVal;
- }
-
-
-
-
- /* xmit:,...
- *
- * Send string to remote and wait for ack. Returns negative error code if
- * failure, otherwise returns positive number of milliseconds required for
- * transfer.
- */
- - (int)xmit:(char *)fmt, ...
- {
- va_list args;
- int retVal;
- char localBuf[SCD_MAX_PACKETSIZE];
-
- va_start(args, fmt);
- vsprintf(localBuf, fmt, args);
- va_end(args);
- retVal = [self sendPkt:DATA_PKT data:localBuf len:strlen(localBuf) pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
-
-
- /* xmit:len:
- *
- * Send fixed-length binary character array to remote and wait for ack. Returns
- * negative error code if failure, otherwise returns positive number of milliseconds
- * required for transfer.
- */
- - (int)xmit:(char *)buf len:(int)len
- {
- int retVal;
-
- retVal = [self sendPkt:DATA_PKT data:buf len:len pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
- /* xmitOOL:,...
- *
- * Send string to remote and wait for ack. Returns negative error code or
- * positive number of microseconds required for transfer. If remote has
- * a delegate, the delegate is sent the data via the -oolMessage:len: msg.
- *
- */
- - (int)xmitOOL:(char *)fmt, ...
- {
- va_list args;
- int retVal;
- char localBuf[SCD_MAX_PACKETSIZE];
-
- va_start(args, fmt);
- vsprintf(localBuf, fmt, args);
- va_end(args);
- retVal = [self sendPkt:OOL_PKT data:localBuf len:strlen(localBuf) pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
-
- /* xmitOOL:len:
- *
- * Send fixed-length binary character array to remote and wait for ack. Returns
- * negative error code if failure, otherwise returns positive number of milliseconds
- * required for transfer. If remote has a delegate, the delegate is sent the data
- * via the -oolMessage:len: msg.
- */
- - (int)xmitOOL:(char *)buf len:(int)len
- {
- int retVal;
-
- retVal = [self sendPkt:OOL_PKT data:buf len:len pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
-
-
- /* test:,...
- *
- * Like xmit:,..., but sends a test packet instead of a data packet. Test
- * packets are immediatly acknowledged by the remote's receiving thread (if
- * it is up and running), and the packet is not forwarded to the receiving
- * application. Returns negative error code or positive number of
- * microseconds required for transfer.
- */
- - (int)test:(char *)fmt, ...
- {
- va_list args;
- int retVal;
- char localBuf[SCD_MAX_PACKETSIZE];
-
- va_start(args, fmt);
- vsprintf(localBuf, fmt, args);
- va_end(args);
- retVal = [self sendPkt:TEST_PKT data:localBuf len:strlen(localBuf) pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
-
-
- /* test:len:
- *
- * Like xmit:len:, but sends a test packet instead of a data packet. Test
- * packets are immediatly acknowledged by the remote's receiving thread (if
- * it is up and running), and the packet is not forwarded to the receiving
- * application. Returns negative error code or positive number of
- * microseconds required for transfer.
- */
- - (int)test:(char *)buf len:(int)len
- {
- int retVal;
-
- retVal = [self sendPkt:TEST_PKT data:buf len:len pktId:NEXT_ID_CHAR(xmitPktId)];
- return retVal;
- }
-
-
-
-
-
- /* processPkt:len:
- *
- * returns zero or greater length if valid data packet, -1 otherwise.
- */
- -(int)processPkt:(char *)pkt len:(int)len
- {
- int i,
- sum,
- retVal = (-1);
- char buf[16],
- *bp;
-
- bp = pkt+(len-CSUM_CHARS-1);
- for (i=0;i<CSUM_CHARS;i++)
- buf[i] = *bp++;
- buf[i] = '\0';
- sscanf(buf,"%x", (unsigned *)&sum);
- if (sum != sumCheck((u_char *)&pkt[1], len-(CSUM_CHARS+2))) { /* bad packet. */
- [self sendPkt:NACK_PKT data:NULL len:0 pktId:NACK_ID];
- if (debugFlag>1)
- fprintf(stderr,"\nReceived Bad Pkt:%s\n", stringForCharArray(pkt,len));
- } else {
- if (pkt[1] == ACK_PKT) {
- asciiAckDPktID = pkt[2];
- } else if (pkt[1] == NACK_PKT) {
- nackDPktFlag++;
- } else if ((pkt[1] == DATA_PKT) || (pkt[1] == OOL_PKT) || (pkt[1] == TEST_PKT)) {
- [self sendPkt:ACK_PKT data:NULL len:0 pktId:pkt[2]];
- if (((pkt[1] == DATA_PKT) || (pkt[1] == OOL_PKT)) && (asciiRcvDPktID != pkt[2])) { /* then this is a new data packet. */
- if (debugFlag>1)
- fprintf(stderr,"\nReceived new Pkt(old:%c, new:%c):%s\n", asciiRcvDPktID, pkt[2], stringForCharArray(pkt,len));
- asciiRcvDPktID = pkt[2];
- if (pkt[1] == OOL_PKT) {
- if (delegate) {
- retVal = stringDecode(&pkt[3], len-(CSUM_CHARS+4), pkt); /* warning! in-place de-packetization. */
- pkt[retVal]='\0';
- [delegate oolMessage:pkt len:retVal];
- retVal = (-1);
- }
- } else {
- retVal = stringDecode(&pkt[3], len-(CSUM_CHARS+4), pkt); /* warning! in-place de-packetization. */
- pkt[retVal]='\0';
- }
- } else if (debugFlag>1) {
- fprintf(stderr,"\nReceived duplicate Pkt:%s\n", stringForCharArray(pkt,len));
- }
- }
- }
-
- return retVal;
- }
-
-
-
-
- /* pollRecvBufferReady
- *
- * Returns 1 if recv Buffer contains data and is ready to be read, otherwise returns 0.
- */
- - (int) pollRecvBufferReady
- {
- int retVal;
- if (dataPktLen < 0)
- retVal = 0;
- else
- retVal = 1;
- return retVal;
- }
-
-
-
-
- /* - recv:maxLen:
- *
- * Returns negative error code if failure, otherwise returns positive
- * number of characters received.
- */
- - (int) recv:(char *)str maxLen:(int)len
- {
- int elapsedTime=0;
- int retVal;
- struct timeval t = {0,0};
-
- microTimeStampA(&t); /* Set start time. */
- for (retVal = 0;!retVal;) {
- if (dataPktLen < 0) {
- elapsedTime = microTimeStampA(&t)/1000;
- if (recvTimeoutMsecs && ((elapsedTime > recvTimeoutMsecs) || (elapsedTime < 0))) {
- if (debugFlag)
- fprintf(stderr,"[RTO]");
- retVal=SCD_TIMEOUT_ERR;
- } else {
- cthread_yield();
- }
- } else {
- break;
- }
- }
- if (!retVal) {
- if (dataPktLen > len) {
- fprintf(stderr,"\nrecv:maxLen: actual packet length:%d exceeds expected:%d\n",dataPktLen, len);
- retVal = SCD_LENGTH_ERR;
- } else {
- bcopy(recvBuf[RCV_OUT_BASKET], str, dataPktLen);
- retVal = dataPktLen;
- if (dataPktLen < (len-1))
- str[dataPktLen] = '\0';
- dataPktLen = (-1);
- }
- }
- return retVal;
- }
-
-
-
- /* - setPort:atRate:
- *
- * Change port and/or transmission parameters.
- * Return 0 on success and negative error code if fail.
- */
- - (int)setPort:(sccPortType)thePort atRate:(baudRateType)theRate
- {
- struct sgttyb sg;
- int retVal =0;
-
- if ((fd > 0) && (thePort != port)) {
- close(fd);
- fd = (-1);
- }
- if (fd < 0 ) {
- if ((fd = open(portString(thePort), O_RDWR)) < 0) {
- fprintf(stderr,"\nopen() failed on %s:%s\n", portString(thePort),
- strerror(errno));
- retVal = SCD_OPEN_ERR;
- } else {
- /* prevent any other unauthorized port accesses */
- if (ioctl(fd, TIOCEXCL, (void*)nil) < 0) {
- fprintf(stderr,
- "\nioctl(TIOCEXCL) failed on %s:%s\n", portString(thePort),
- strerror(errno));
- retVal = SCD_OPEN_ERR;
- }
- }
- }
-
- if (!retVal) {
- if (ioctl(fd, TIOCGETP, &sg) < 0) {
- fprintf(stderr,
- "\nioctl(TIOCGETP) failed on %s:%s\n", portString(thePort),
- strerror(errno));
- retVal = SCD_OPEN_ERR;
- } else {
- sg.sg_ispeed = baudRateCode(theRate);
- sg.sg_ospeed = baudRateCode(theRate);
- sg.sg_flags = RAW;
- sg.sg_flags |= ANYP;
- if (ioctl(fd, TIOCSETP, &sg) < 0) {
- fprintf(stderr,
- "\nioctl(TIOCSETP) failed on %s:%s\n", portString(thePort),
- strerror(errno));
- retVal = SCD_OPEN_ERR;
- }
- }
- }
- if (!retVal) {
- port = thePort;
- baudRate = theRate;
- } else {
- if (fd > 0)
- close(fd);
- port = noPort;
- baudRate = brNone;
- }
- return retVal;
- }
-
-
-
-
- /* - initPort:atRate:
- *
- * open specified port at specified rate. Returns nil on failure.
- */
- - initPort:(sccPortType)thePort atRate:(baudRateType)theRate
- {
- [super init];
- dataPktLen = -1;
- (mutex_t)writeLock=mutex_alloc();
- mutex_init((mutex_t)writeLock);
- (mutex_t)dataPktLock=mutex_alloc();
- mutex_init((mutex_t)dataPktLock);
- fd = (-1);
- maxXmitRetries = XMIT_RETRY_COUNT;
- recvTimeoutMsecs = RECV_TIMEOUT_MSECS;
- if (theRate == brNone) {
- baudRateType rate;
- int timeout;
-
- for (timeout=10,rate = BR_MAX;rate >= BR_MIN_AUTO_CONF;rate--,timeout*=2) {
- if ([self setPort:thePort atRate:rate] < 0) {
- fprintf(stderr,"\nsetPort:atRate:%d failed.\n", rate);
- } else {
- if (ioctl(fd, TIOCCBRK, (void*)nil) < 0) { /* clear break bit. */
- fprintf(stderr,
- "\nioctl(TIOCCBRK) failed on %s:%s\n", portString(thePort),
- strerror(errno));
- }
- xmitAckTimeoutMsecs = timeout;
- if ([self test:"Attempting to determine baud rate for remote interface."] >= 0)
- break;
- if (ioctl(fd, TIOCSBRK, (void*)nil) < 0) { /* set break bit. */
- fprintf(stderr,
- "\nioctl(TIOCSBRK) failed on %s:%s\n", portString(thePort),
- strerror(errno));
- }
- cthread_yield();
- }
- }
- if (rate < BR_MIN_AUTO_CONF) {
- if ([self setPort:thePort atRate:BR_MAX] < 0) {
- [self free];
- self = nil;
- }
- }
- } else if ([self setPort:thePort atRate:theRate]) {
- [self free];
- self = nil;
- }
-
- if (self != nil) {
- xmitAckTimeoutMsecs = XMIT_TIMEOUT_MSECS;
- cthread_detach(cthread_fork((cthread_fn_t)theListener, (any_t)self));
- }
- return self;
- }
-
-
-
- /* -initPort:
- * open specified port and search for match baud rate. Returns nil on failure.
- */
- - initPort:(sccPortType)thePort
- {
- return [self initPort:thePort atRate:brNone];
- }
-
-
-
- /* - init
- *
- * open default port (A) and search for match baud rate. Returns nil on failure.
- */
- - init
- {
- return [self initPort:sccPortA atRate:brNone];
- }
-
-
- -setDelegate:myDelegate
- {
- delegate = myDelegate;
- return self;
- }
-
- -delegate
- {
- return delegate;
- }
-
- @end
-
- /* pktSnooper()
- *
- * Browses first *size characters in pkt. If a pkt is found, it is left-justified in the array
- * and it's length is returned - otherwize zero is returned.
- */
- static int pktSnooper(char *head, int *size, id self)
- {
- char *cp,
- *wp;
- int retVal, i, garbage;
-
- if (debugFlag > 10)
- fprintf(stderr,"\nSnoopy called with %d for %s\n", *size, stringForCharArray(head, *size));
- if (*size > 0 && *head != SOP_CHAR_CODE) {
- if (debugFlag)
- fprintf(stderr,"\nSnoopy Found packet w/o head\n");
- /* [self sendPkt:NACK_PKT data:NULL len:0 pktId:NACK_ID]; */
- for (garbage=i=0,cp=head;i<*size; i++) {
- if (*cp++ == SOP_CHAR_CODE)
- break;
- garbage++;
- }
- if (debugFlag)
- fprintf(stderr,"\nNuking %d Garbage chars\n", garbage);
- *size -= garbage;
- for (i=0,wp=head;i<*size;i++)
- *wp++ = *cp++;
- }
- for (wp=cp=head,garbage=i=0;i<*size;i++) {
- if ((*cp == SOP_CHAR_CODE) && (cp > head)) {
- if (debugFlag)
- fprintf(stderr,"\nNuking %d Garbage chars\n", (int)(cp-head));
- garbage+=(cp-head);
- wp = head;
- *wp++ = *cp++;
- } else if (*cp == EOP_CHAR_CODE) {
- *wp++ = *cp++;
- if (*head == SOP_CHAR_CODE) {
- break;
- }
- } else if (ILLEGAL_CHAR(*cp)) {
- garbage++;
- cp++;
- } else {
- *wp++ = *cp++;
- }
- }
- if (i < (*size))
- retVal = i+1;
- else
- retVal = 0;
- *size -= garbage;
- if (debugFlag > 10)
- fprintf(stderr,"\nSnoopy returning %d for %s with size:%d\n", retVal, stringForCharArray(head, *size),*size);
- return retVal;
- }
-
-
- /* theListener()
- An independent background thread that continuously reads the recv port. Incoming data
- is read into RCV_IN_BASKET buffer. Once an entire pkt is in the buffer, processPkt is
- called to verify the packet's checksum and to build and send an appropriate reply packet.
- If the packet was a DATA packet, then any escape sequences in the data field are processed
- to restore any non PASCII characters and the data field is shifted to the front of the
- buffer and the function returns the length of the data field. The thread then waits until
- RCV_OUT_BASKET is empty (The application thread finishes with the buffer and sets
- dataPktLen= -1) before swapping IN and OUT baskets. The last thing the thread does is set
- dataPktLen to the length of the data buffer to let the application thread know that data is
- available.
- */
- static void theListener(SerialCommDevice *self)
- {
- char *cp, *pkt;
- int i,
- len,
- oldSize,
- newSize;
-
- for (oldSize=newSize=0, pkt=self->recvBuf[RCV_IN_BASKET];;) {
- for (;;) {
- if ((newSize = read(self->fd, pkt+oldSize, SCD_MAX_PACKETSIZE-oldSize)) < 0)
- fprintf(stderr,"\nread() failed on %s:%s\n", portString(self->port), strerror(errno));
- oldSize+=newSize;
- if (debugFlag > 10)
- fprintf(stderr,"\nPkt:%s\n", stringForCharArray(pkt,oldSize));
- if ((len = pktSnooper(pkt, &oldSize, self)) > 0)
- break;
- cthread_yield();
- }
- do {
- cp = &pkt[len];
- oldSize -= len;
- if ((newSize = [self processPkt:pkt len:len]) >= 0) {
- while (self->dataPktLen >= 0) /* wait for primary thread to finish with it's buffer. */
- cthread_yield();
- self->dataPktIndex = !self->dataPktIndex; /* switch buffers. */
- }
-
- pkt=self->recvBuf[RCV_IN_BASKET];
- for (i=0;i<oldSize;i++) /* copy remainder of data into head of buffer. */
- pkt[i] = *cp++;
- if (self->dataPktLen < 0)
- self->dataPktLen = newSize;
- len = pktSnooper(pkt, &oldSize, self);
- } while (len > 0);
- }
- }
-
- @implementation SCDDelegate
- -oolMessage:(char *)bufr len:(int)l {return self;}
- @end
-